<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title></title>
	<style type="text/css">
	*{
		margin: 0;
		padding: 0;
	}
	html,body{
		width: 100%;
		height: 100%;
		overflow: hidden;
		text-align: center;
		background-color: #000;
	}
	#gameCanvas{
		position: fixed;
		left: 0;
		top: 0;
		mix-blend-mode: screen;
		z-index: 1;
	}
	</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<script type="text/javascript">
Array.prototype.forRound = function(fn,axis,self){
	var that = this, i = axis[0], j=axis[1];
	f(i-1,j-1);f(i-1,j);f(i-1,j+1);f(i,j-1);
	if(self){f(i,j)}f(i,j+1);f(i+1,j-1);f(i+1,j);f(i+1,j+1);
	function f(i,j){
		// 确保不访问非法内存
		if(i < 0 || i >= that.length || j < 0 || j >= that[0].length){
			return 0
		}
		fn(that[i][j],[i,j],that)
	}
}</script>
<script type="text/javascript">
!function(w,e){
	function Game(option){
		Object.assign(this,{
			el: "#gameCanvas",
			mainWidth: 0,
			mainHeight: 0,
			timer: null,
			pList: [],
			nX: 41,
			nY: 41,
			ms: 0,
			mouse: [],
			isPress: false
		},option)
		this.init()
	}
	Game.prototype.expend = function(obj){Object.assign(this,obj)}
	Game.prototype.expend({
		// 初始化
		init(){
			this.canvas = e.querySelector(this.el)
			this.canvas.isUsed = true
			this.mainWidth = w.innerWidth
			this.mainHeight = w.innerHeight
			this.canvas.setAttribute("width",this.mainWidth)
			this.canvas.setAttribute("height",this.mainHeight)
			this.cx = this.canvas.getContext("2d")
			this.addEvents()
			this.start()
		},
		// 绑定事件
		addEvents(){
			this.windowKeydown = evt => {
				var k = evt.key
				if(k === "a" || k === "A" || k === "ArrowLeft"){
					this.turn(-1,0)
				}else if(k === "d" || k === "D" || k === "ArrowRight"){
					this.turn(1,0)
				}else if(k === "w" || k === "W" || k === "ArrowUp"){
					this.turn(0,-1)
				}else if(k === "s" || k === "S" || k === "ArrowDown"){
					this.turn(0,1)
				}
			}
			this.windowMousemove = evt => {
				this.mouse[0] = evt.pageX
				this.mouse[1] = evt.pageY
			}
			this.windowResize = () => {
				this.mainWidth = w.innerWidth
				this.mainHeight = w.innerHeight
				this.canvas.setAttribute("width",this.mainWidth)
				this.canvas.setAttribute("height",this.mainHeight)
			}
			this.windowMousedown = () => {
				this.isPress = true
			}
			this.windowMouseup = () => {
				this.isPress = false
			}
			w.addEventListener("mousemove",this.windowMousemove)
			w.addEventListener("mousedown",this.windowMousedown)
			w.addEventListener("mouseup",this.windowMouseup)
			w.addEventListener("resize",this.windowResize)
			w.addEventListener("keydown",this.windowKeydown)
		},
		start(){
			this.cTimer = setInterval(() => {
				if(this.isPress+1){
					this.pList.unshift({
						x: this.mouse[0],
						y: this.mouse[1],
						oX: 0,
						oY: 0,
						age: 0,
						arrow: [Math.random()-.5,Math.random()+.5],
						R: Math.random() * 2 + 1,
						hue: Math.floor(Math.random()*360),
						lineable: Math.floor(Math.random()*1.5),
						cut: 200
					})
					this.pList.unshift({
						x: this.mouse[0],
						y: this.mouse[1],
						oX: 0,
						oY: 0,
						age: 0,
						arrow: [Math.random()-.5,Math.random()*.5],
						R: .5,
						hue: Math.floor(Math.random()*360),
						lineable: 1,
						cut: 20
					})
				}
				if(this.pList.length > 2000){
					this.pList.pop()
					this.pList.pop()
				}
			},10)
			this.cx.fillStyle = "#fff"
			this.cx.strokeStyle = "#fff"
			this.cx.lineWidth = .2
			this.draw(() => {
				this.cx.shadowBlur = 0
				this.cx.fillStyle = "#00000052"
				this.cx.fillRect(0,0,this.mainWidth,this.mainHeight)
				// 连线
				var lList = []
				var exLList = []
				this.pList.forEach(p => {
					if(this.getAll(...this.mouse,p.oX,p.oY) < p.cut && p.lineable){
						lList.push([p.oX,p.oY])
					}
				})
				lList.forEach((oL,oI) => {
					lList.forEach((l,i) => {
						if(oI !== i && exLList.length < 10){
							if(Math.random()>.5){
								exLList.push([oL,l])
							}
						}
					})
				})
				lList.forEach(l => {
					this.cx.beginPath()
					this.cx.moveTo(...l)
					this.cx.lineTo(...this.mouse)
					this.cx.closePath()
					this.cx.stroke()
				})
				exLList.forEach(l => {
					this.cx.beginPath()
					this.cx.moveTo(...l[0])
					this.cx.lineTo(...l[1])
					this.cx.closePath()
					this.cx.stroke()
				})
				// 粒子
				this.pList.forEach(p => {
					p.age += 16
					var x = p.arrow[0] * p.age / 5
					var y = p.arrow[1] * Math.pow(p.age/500,2) * 100
					p.oX = p.x + x
					p.oY = p.y + y
					this.cx.fillStyle = "hsl("+p.hue+",50%,80%)"
					this.cx.beginPath()
					this.cx.arc(p.x + x, p.y + y, p.R, 0, Math.PI*2)
					this.cx.closePath()
					this.cx.fill()
				})
				var lightness = Math.sin(this.ms / 6) + 1.1
				this.cx.shadowBlur = lightness * 8
				this.cx.shadowColor = "#ffa6a6"
				this.cx.beginPath()
				this.cx.arc(...this.mouse,4,0,Math.PI*2)
				this.cx.closePath()
				this.cx.fillStyle = "#fff9f9"
				this.cx.fill()
			})
		},
		getAll(x1,y1,x2,y2){
			return Math.sqrt(Math.pow(x2-x1,2) + Math.pow(y2-y1,2))
		},
		// 销毁实例
		destroy(){
			this.timer && clearInterval(this.timer)
			this.fTimer && clearInterval(this.fTimer)
			this.cTimer && clearInterval(this.cTimer)
			this.cx.clearRect(0,0,this.mainWidth,this.mainHeight)
			this.canvas.isUsed = false
			w.removeEventListener("mousemove",this.windowMousemove)
			w.removeEventListener("mousedown",this.windowMousedown)
			w.removeEventListener("mouseup",this.windowMouseup)
			w.removeEventListener("resize",this.windowResize)
			w.removeEventListener("keydown",this.windowKeydown)
		},
		// 主绘制函数
		draw(frame,isSingle){
			this.timer && (clearInterval(this.timer),this.timer = null)
			var main = () => {
				this.ms = performance.now()
				this.cx.save()
				// this.cx.clearRect(0,0,this.mainWidth,this.mainHeight)
				frame()
				this.cx.restore()
			}
			if(isSingle){
				main()
			}else{
				this.timer = setInterval(main,16)
			}
		}
	})
	w.Game = Game
}(window,document)
var gameDemo = new Game()
</script>
</body>
</html>